// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 这个文件将不断发展，因为我们计划在字典中混合使用模板和传递
// 的功能。

package noder

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/objw"
	"cmd/compile/internal/reflectdata"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/internal/obj"
	"cmd/internal/src"
	"fmt"
	"go/constant"
)

// 启用额外的一致性检查。
const doubleCheck = false

func assert(p bool) {
	base.Assert(p)
}

// 用于输出有关字典格式和实例化字典的调试信息
// （类型arg、派生类型、子字典和itab条目）。
var infoPrintMode = false

func infoPrint(format string, a ...interface{}) {
	if infoPrintMode {
		fmt.Printf(format, a...)
	}
}

var geninst genInst

func BuildInstantiations() {
	geninst.instInfoMap = make(map[*types.Sym]*instInfo)
	geninst.buildInstantiations()
	geninst.instInfoMap = nil
}

// buildInstances扫描函数以查找泛型函数调用和方法，
// 创建所需的实例化。它还为所有在实例化过程中遇到的
// 完全实例化的泛型类型或新的
// 创建实例化方法。它扫描类型检查中的所有声明
// 。目标在扫描创建的任何新实例之前，先进行Decls。
func (g *genInst) buildInstantiations() {
	// 实例化我们到目前为止看到的实例化泛型类型的方法。
	g.instantiateMethods()

	// 扫描所有currentdecls以查找对泛型函数/方法的调用。
	n := len(typecheck.Target.Decls)
	for i := 0; i < n; i++ {
		g.scanForGenCalls(typecheck.Target.Decls[i])
	}

	// 扫描由于g.InstanceMethods（）和当前DECL的
	// 扫描而创建的所有新实例化。这个循环故意运行，直到没有创建新的
	// 实例化为止。
	for i := 0; i < len(g.newInsts); i++ {
		g.scanForGenCalls(g.newInsts[i])
	}

	g.finalizeSyms()

	// 所有实例化和字典都已创建。现在，检查一下
	// 每一个新的实例化，并转换使用字典所需的各种操作。
	l := len(g.newInsts)
	for _, fun := range g.newInsts {
		info := g.instInfoMap[fun.Sym()]
		g.dictPass(info)
		if doubleCheck {
			ir.Visit(info.fun, func(n ir.Node) {
				if n.Op() != ir.OCONVIFACE {
					return
				}
				c := n.(*ir.ConvExpr)
				if c.X.Type().HasShape() && !c.X.Type().IsInterface() {
					ir.Dump("BAD FUNCTION", info.fun)
					ir.Dump("BAD CONVERSION", c)
					base.Fatalf("converting shape type to interface")
				}
			})
		}
		if base.Flag.W > 1 {
			ir.Dump(fmt.Sprintf("\ndictpass %v", info.fun), info.fun)
		}
	}
	assert(l == len(g.newInsts))
	g.newInsts = nil
}

// ScanfegnCalls扫描单个函数（或全局赋值），查找
// 对泛型函数/方法的引用。在每个这样的引用中，它都会创建任何
// 所需的实例化并转换引用。
func (g *genInst) scanForGenCalls(decl ir.Node) {
	switch decl.Op() {
	case ir.ODCLFUNC:
		if decl.Type().HasTParam() {
			// 跳过任何通用函数
			return
		}
		// 下面的transformCall（）取决于设置的CurFunc。这些都是各种各样的全局赋值，其右边可能包含函数
		ir.CurFunc = decl.(*ir.Func)

	case ir.OAS, ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV, ir.OASOP:
		// 实例化。

	default:
		// 顶层的其他可能操作是ODCLCONST 
		// 和ODCLTYPE，它们没有任何函数
		// 实例化。
		return
	}

	// 使用泛型函数/方法搜索任何函数引用。然后
	// 如果尚未创建所需的实例化函数，则创建该函数，然后
	// 更改为直接调用该函数。
	modified := false
	closureRequired := false
	// 如果我们正在扫描一个实例化函数
	declInfo := g.instInfoMap[decl.Sym()]

	ir.Visit(decl, func(n ir.Node) {
		if n.Op() == ir.OFUNCINST {
			// 泛型F，则declInfo将不完全为零，而不是立即调用
			closureRequired = true
		}
		if (n.Op() == ir.OMETHEXPR || n.Op() == ir.OMETHVALUE) && len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) {
			// T.M或x.M，其中T或x是泛型的，但不是立即调用
			// 的。如果选择的方法是
			// 实际上是嵌入式接口字段，则不需要。
			closureRequired = true
		}
		if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST {
			// 我们发现了一个使用泛型函数
			// 实例化的函数调用。
			call := n.(*ir.CallExpr)
			inst := call.X.(*ir.InstExpr)
			nameNode, isMeth := g.getInstNameNode(inst)
			targs := typecheck.TypesOf(inst.Targs)
			st := g.getInstantiation(nameNode, targs, isMeth).fun
			dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, nameNode, targs, isMeth)
			if infoPrintMode {
				dictkind := "Main dictionary"
				if usingSubdict {
					dictkind = "Sub-dictionary"
				}
				if inst.X.Op() == ir.OMETHVALUE {
					fmt.Printf("%s in %v at generic method call: %v - %v\n", dictkind, decl, inst.X, call)
				} else {
					fmt.Printf("%s in %v at generic function call: %v - %v\n", dictkind, decl, inst.X, call)
				}
			}

			// 立即转换调用，将OCALL更改为
			// OCALLFUNC并执行typecheckast/assignconvfn。执行
			// 在安装实例化之前执行此操作，因此我们正在
			// 检查
			// typecheckaste中的非形状参数类型。
			transformCall(call)

			// 用直接引用
			// 新的模板函数
			call.X = st.Nname
			if inst.X.Op() == ir.OMETHVALUE {
				// 当我们创建一个方法
				// 调用的实例化时，我们将其设为函数。因此，将
				// 接收器移动到函数
				// 调用的第一个参数。
				call.Args.Prepend(inst.X.(*ir.SelectorExpr).X)
			}

			// 将字典添加到参数列表中。
			call.Args.Prepend(dictValue)
			modified = true
		}
		if n.Op() == ir.OCALLMETH && n.(*ir.CallExpr).X.Op() == ir.ODOTMETH && len(deref(n.(*ir.CallExpr).X.Type().Recv().Type).RParams()) > 0 {
			// 对泛型类型的方法调用，该泛型类型通过模板化实例化。
			// 对显式实例化类型的方法调用将有一个OFUNINST 
			// 的值，并在上面进行处理。
			call := n.(*ir.CallExpr)
			meth := call.X.(*ir.SelectorExpr)
			targs := deref(meth.Type().Recv().Type).RParams()

			t := meth.X.Type()
			baseType := deref(t).OrigType()
			var gf *ir.Name
			for _, m := range baseType.Methods().Slice() {
				if meth.Sel == m.Sym {
					gf = m.Nname.(*ir.Name)
					break
				}
			}

			// 立即转换调用，将OCALL 
			// 更改为OCALLFUNC，并执行typechecaste/assignconvfn。
			transformCall(call)

			st := g.getInstantiation(gf, targs, true).fun
			dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, gf, targs, true)
			// 我们必须使用子字典，因为这是
			// 一个通用方法调用。
			assert(usingSubdict)

			// 通过将
			// 字典和接收器附加到参数，将转换为函数调用。
			call.SetOp(ir.OCALLFUNC)
			call.X = st.Nname
			call.Args.Prepend(dictValue, meth.X)
			modified = true
		}
	})

	// 如果我们找到一个对非
	// 立即调用的泛型实例化的引用，然后再次遍历decl的节点（使用
	// 而不是Visit），在这里我们实际上将对实例化的
	// 引用更改为捕获
	// 字典的闭包，然后执行直接调用。
	// EditChildren比拜访贵，所以我们只做这个
	// 在没有相应
	// 调用的情况下，OFUNCINST很少出现。
	if closureRequired {
		modified = true
		var edit func(ir.Node) ir.Node
		var outer *ir.Func
		if f, ok := decl.(*ir.Func); ok {
			outer = f
		}
		edit = func(x ir.Node) ir.Node {
			if x.Op() == ir.OFUNCINST {
				child := x.(*ir.InstExpr).X
				if child.Op() == ir.OMETHEXPR || child.Op() == ir.OMETHVALUE {
					// 在child（x.x）上调用EditChildren，
					// 而不是x，这样我们就不会在
					// buildClosure（）。
					// METHEXPR/METHVALUE节点上执行
					ir.EditChildren(child, edit)
					return g.buildClosure(outer, x)
				}
			}
			ir.EditChildren(x, edit)
			switch {
			case x.Op() == ir.OFUNCINST:
				return g.buildClosure(outer, x)
			case (x.Op() == ir.OMETHEXPR || x.Op() == ir.OMETHVALUE) &&
				len(deref(x.(*ir.SelectorExpr).X.Type()).RParams()) > 0 &&
				!types.IsInterfaceMethod(x.(*ir.SelectorExpr).Selection.Type):
				return g.buildClosure(outer, x)
			}
			return x
		}
		edit(decl)
	}
	if base.Flag.W > 1 && modified {
		ir.Dump(fmt.Sprintf("\nmodified %v", decl), decl)
	}
	ir.CurFunc = nil
	// 我们可能已经看到了新的完全实例化的泛型类型，而
	// 正在实例化上述
	// 函数中所需的任何函数/方法。如果是这样，请实例化这些类型的所有方法
	// （这将导致在循环中扫描更多的函数/方法）。
	g.instantiateMethods()
}

// buildClosure生成一个闭包来实现x，一个泛型类型的uninst或OMETHEXPR/OMETHVALUE 
// 。outer是包含函数（如果闭包是
// 而不是函数，则为nil）。
func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
	pos := x.Pos()
	var target *ir.Func   // 目标实例化函数/方法
	var dictValue ir.Node // 字典使用
	var rcvrValue ir.Node // 接收器，如果方法值
	typ := x.Type()       // 闭包类型
	var outerInfo *instInfo
	if outer != nil {
		outerInfo = g.instInfoMap[outer.Sym()]
	}
	usingSubdict := false
	valueMethod := false
	if x.Op() == ir.OFUNCINST {
		inst := x.(*ir.InstExpr)

		// 我们正在实例化的类型参数。
		targs := typecheck.TypesOf(inst.Targs)

		// 查找泛型函数/方法。
		var gf *ir.Name
		if inst.X.Op() == ir.ONAME {
			// 实例化通用函数调用。
			gf = inst.X.(*ir.Name)
		} else if inst.X.Op() == ir.OMETHVALUE {
			// 实例化方法值x.M.
			se := inst.X.(*ir.SelectorExpr)
			rcvrValue = se.X
			gf = se.Selection.Nname.(*ir.Name)
		} else {
			panic("unhandled")
		}

		// 目标是我们试图调用的实例化函数。
		// 对于函数，目标需要一个字典作为其第一个参数。
		// 对于方法值，目标期望字典和接收方
		// 作为其前两个参数。
		// dictValue是用于dictionary参数的值。
		target = g.getInstantiation(gf, targs, rcvrValue != nil).fun
		dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, rcvrValue != nil)
		if infoPrintMode {
			dictkind := "Main dictionary"
			if usingSubdict {
				dictkind = "Sub-dictionary"
			}
			if rcvrValue == nil {
				fmt.Printf("%s in %v for generic function value %v\n", dictkind, outer, inst.X)
			} else {
				fmt.Printf("%s in %v for generic method value %v\n", dictkind, outer, inst.X)
			}
		}
	} else { // ir。OMETHEXPR或ir。METHVALUE 
		// 方法表达式T.M，其中T是泛型类型。
		se := x.(*ir.SelectorExpr)
		targs := deref(se.X.Type()).RParams()
		if len(targs) == 0 {
			panic("bad")
		}
		if x.Op() == ir.OMETHVALUE {
			rcvrValue = se.X
		}

		// se。X.Type（）是方法表达式的顶级类型。要正确处理包含嵌入字段的方法表达式，请使用se的接收器类型查找下面的通用方法。选择，因为这将是实际包含
		// 方法的类型。
		recv := deref(se.Selection.Type.Recv().Type)
		if len(recv.RParams()) == 0 {
			// 实际包含该方法的嵌入类型不是
			// 实际上是泛型的，因此不需要构建闭包。
			return x
		}
		baseType := recv.OrigType()
		var gf *ir.Name
		for _, m := range baseType.Methods().Slice() {
			if se.Sel == m.Sym {
				gf = m.Nname.(*ir.Name)
				break
			}
		}
		if !gf.Type().Recv().Type.IsPtr() {
			// 记住if value方法，这样我们就可以检测（*T）。我是凯斯。
			valueMethod = true
		}
		target = g.getInstantiation(gf, targs, true).fun
		dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, true)
		if infoPrintMode {
			dictkind := "Main dictionary"
			if usingSubdict {
				dictkind = "Sub-dictionary"
			}
			fmt.Printf("%s in %v for method expression %v\n", dictkind, outer, x)
		}
	}

	// 构建一个闭包来实现函数实例化。
	// 
	// func f[T any]（int，int）（int，int）{……随便……}
	// 
	// 那么任何对f[int]的未直接调用的引用都会被重写为
	// 
	// 。格言：=。。。要使用的字典。。。
	// func（a0，a1 int）（r0，r1 int）{
	// return.inst.f[int]（.dictN，a0，a1）
	// }
	// 
	// 。。。。
	// func（rcvr g[T]）f（a0，a1 int）（r0，r1 int）{…}
	// 
	// 对g[int]的任何引用。未直接调用的f被重写为
	// 
	// 。格言：=。。。要使用的字典。。。
	// func（rcvr g[int]，a0，a1 int）（r0，r1 int）{
	// return.inst.g[int].f（.dictN，rcvr，a0，a1）
	// }
	// 
	// 还有方法值
	// 
	// var x g[int]
	// 。格言：=。。。要使用的字典。。。
	// x2:=x 
	// func（a0，a1 int）（r0，r1 int）{
	// return.inst.g[int].f（.dictN，x2，a0，a1）
	// }

	// /创建一个新的内部函数。这是我们要用的字典。
	fn, formalParams, formalResults := startClosure(pos, outer, typ)

	// 它可能是一个常数，可能是外部函数的字典，也可能是从外部函数的字典中获取的子字典。
	// 对于后者，dictVar是外部函数作用域中的一个变量，设置为子字典
	// 从外部函数的字典中读取。
	var dictVar *ir.Name
	var dictAssign *ir.AssignStmt
	if outer != nil {
		dictVar = ir.NewNameAt(pos, typecheck.LookupNum(typecheck.LocalDictName, g.dnum))
		g.dnum++
		dictVar.Class = ir.PAUTO
		typed(types.Types[types.TUINTPTR], dictVar)
		dictVar.Curfn = outer
		dictAssign = ir.NewAssignStmt(pos, dictVar, dictValue)
		dictAssign.SetTypecheck(1)
		dictVar.Defn = dictAssign
		outer.Dcl = append(outer.Dcl, dictVar)
	}
	// 将接收者分配给临时用户。
	var rcvrVar *ir.Name
	var rcvrAssign ir.Node
	if rcvrValue != nil {
		rcvrVar = ir.NewNameAt(pos, typecheck.LookupNum(".rcvr", g.dnum))
		g.dnum++
		typed(rcvrValue.Type(), rcvrVar)
		rcvrAssign = ir.NewAssignStmt(pos, rcvrVar, rcvrValue)
		rcvrAssign.SetTypecheck(1)
		rcvrVar.Defn = rcvrAssign
		if outer == nil {
			rcvrVar.Class = ir.PEXTERN
			typecheck.Target.Decls = append(typecheck.Target.Decls, rcvrAssign)
			typecheck.Target.Externs = append(typecheck.Target.Externs, rcvrVar)
		} else {
			rcvrVar.Class = ir.PAUTO
			rcvrVar.Curfn = outer
			outer.Dcl = append(outer.Dcl, rcvrVar)
		}
	}

	// 构建闭包体。这涉及到直接调用包装函数
	// 和附加的dictionary参数。

	// 首先，找出dictionary参数。
	var dict2Var ir.Node
	if usingSubdict {
		// 捕获子字典在外部函数
		dict2Var = ir.CaptureName(pos, fn, dictVar)
		typed(types.Types[types.TUINTPTR], dict2Var)
	} else {
		// 静态字典中计算，因此可以直接用于闭包
		dict2Var = dictValue
	}
	// 还捕获接收器变量。
	var rcvr2Var *ir.Name
	if rcvrValue != nil {
		rcvr2Var = ir.CaptureName(pos, fn, rcvrVar)
	}

	// 构建要在闭包内调用的参数。
	var args []ir.Node

	// 首先是dictionary参数。
	args = append(args, dict2Var)
	// 然后是接收器。
	if rcvrValue != nil {
		args = append(args, rcvr2Var)
	}
	// 然后是所有其他参数（包括方法表达式的接收器）。
	for i := 0; i < typ.NumParams(); i++ {
		if x.Op() == ir.OMETHEXPR && i == 0 {
			// 如果我们在做一个方法表达式，我们需要显式遍历接收方
			// 参数中的任何嵌入字段，以便调用方法实例化。
			arg0 := formalParams[0].Nname.(ir.Node)
			arg0 = typecheck.AddImplicitDots(ir.NewSelectorExpr(x.Pos(), ir.OXDOT, arg0, x.(*ir.SelectorExpr).Sel)).X
			if valueMethod && arg0.Type().IsPtr() {
				// 用于处理（*T）。M case：如果我们有一个指针
				// 在跟踪所有嵌入字段之后，我们有一个接收器，
				// 但是它是一个值方法，添加一个星形运算符。
				arg0 = ir.NewStarExpr(arg0.Pos(), arg0)
			}
			args = append(args, arg0)
		} else {
			args = append(args, formalParams[i].Nname.(*ir.Name))
		}
	}

	// 构建调用自身。
	var innerCall ir.Node = ir.NewCallExpr(pos, ir.OCALL, target.Nname, args)
	innerCall.(*ir.CallExpr).IsDDD = typ.IsVariadic()
	if len(formalResults) > 0 {
		innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall})
	}
	// 完成封闭体的建造。
	ir.CurFunc = fn
	// TODO:直接在此处设置类型，而不是使用类型检查。Stmt 
	typecheck.Stmt(innerCall)
	ir.CurFunc = nil
	fn.Body = []ir.Node{innerCall}

	// 我们已经完成了捕获的字典（以及方法值的接收器）。
	ir.FinishCaptureNames(pos, outer, fn)

	// 引用我们的新内部函数进行闭包。
	c := ir.UseClosure(fn.OClosure, typecheck.Target)
	var init []ir.Node
	if outer != nil {
		init = append(init, dictAssign)
	}
	if rcvrValue != nil {
		init = append(init, rcvrAssign)
	}
	return ir.InitExpr(init, c)
}

// 实例化方法实例化了
// 已添加到类型检查中的所有完全实例化的泛型类型的所有方法（以及相关字典）。输入类型列表。
// 直到不再向typecheck添加任何类型为止。输入类型列表。
func (g *genInst) instantiateMethods() {
	for {
		instTypeList := typecheck.GetInstTypeList()
		if len(instTypeList) == 0 {
			break
		}
		typecheck.ClearInstTypeList()
		for _, typ := range instTypeList {
			assert(!typ.HasShape())
			// 根据需要标记运行时类型，因为这样可以确保当此
			// 编译器输出所需的矮符号。
			// 实例化类型的包与本地
			// 包不同时，
			typecheck.NeedRuntimeType(typ)
			// 在基泛型类型上查找方法，因为方法可能无法在导入的实例化类型上设置。
			baseType := typ.OrigType()
			for j, _ := range typ.Methods().Slice() {
				if baseType.Methods().Slice()[j].Nointerface() {
					typ.Methods().Slice()[j].SetNointerface(true)
				}
				baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name)
				// 急切地生成实现这些方法的实例化和字典。
				// 我们这里不使用实例化，只生成它们（以及它们生成的任何
				// 进一步的实例化，等等）。
				// 注意，我们没有为实例化的
				// 类型上的任何方法设置Func。他们的签名不匹配，所以会让人困惑。
				// 直接方法调用直接进入上面实现的实例化。
				// 间接方法调用使用reflectcall中生成的包装器。这些包装器
				// 将在需要时使用这些实例化（用于接口表或反射）。
				_ = g.getInstantiation(baseNname, typ.RParams(), true)
				_ = g.getDictionarySym(baseNname, typ.RParams(), true)
			}
		}
	}
}

// getInstNameNode返回正在实例化的方法或函数的名称节点，如果正在实例化一个方法，则返回一个布尔值。
func (g *genInst) getInstNameNode(inst *ir.InstExpr) (*ir.Name, bool) {
	if meth, ok := inst.X.(*ir.SelectorExpr); ok {
		return meth.Selection.Nname.(*ir.Name), true
	} else {
		return inst.X.(*ir.Name), false
	}
}

// getDictOrSubdict根据需要，为
// 实例化（由instInfo描述）中的方法/函数调用或引用（节点n）返回正在访问子字典
// 或主/静态字典的节点，还返回一个布尔值，指示是否访问了
// 子字典。nameNode是
// 调用/引用的特定函数或方法，targs是类型参数。
func (g *genInst) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.Name, targs []*types.Type, isMeth bool) (ir.Node, bool) {
	var dict ir.Node
	usingSubdict := false
	if declInfo != nil {
		entry := -1
		for i, de := range declInfo.dictInfo.subDictCalls {
			if n == de.callNode {
				entry = declInfo.dictInfo.startSubDict + i
				break
			}
		}
		// 如果找不到条目，则可能是此节点没有依赖于类型参数的任何类型参数，因此我们需要一个主
		// 字典，而不是子字典。
		if entry >= 0 {
			dict = getDictionaryEntry(n.Pos(), declInfo.dictParam, entry, declInfo.dictInfo.dictLen)
			usingSubdict = true
		}
	}
	if !usingSubdict {
		dict = g.getDictionaryValue(n.Pos(), nameNode, targs, isMeth)
	}
	return dict, usingSubdict
}

// checkFetchBody检查是否可以提取泛型体，但尚未加载
// 尚未加载。如果是的话，它会导入身体。
func checkFetchBody(nameNode *ir.Name) {
	if nameNode.Func.Body == nil && nameNode.Func.Inl != nil {
		// 如果除了Func还没有尸体。如果存在Inl，那么我们可以
		// 导入整个通用体。
		assert(nameNode.Func.Inl.Cost == 1 && nameNode.Sym().Pkg != types.LocalPkg)
		typecheck.ImportBody(nameNode.Func)
		assert(nameNode.Func.Inl.Body != nil)
		nameNode.Func.Body = nameNode.Func.Inl.Body
		nameNode.Func.Dcl = nameNode.Func.Inl.Dcl
	}
}

// getInstantiation获取函数或方法nameNode的实例化和字典
// 。如果实例化的函数尚未被缓存，那么它将调用genericSubst来创建新的实例化。
func (g *genInst) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth bool) *instInfo {
	if nameNode.Func == nil {
		// If nameNode。Func为nil，这必须是对
		// 导入的实例化类型的方法的引用。我们已经在完全实例化的类型上调用了
		// g.InstanceMethods（），因此下面的
		// g.instInfoMap[sym]将为非零。
		rcvr := nameNode.Type().Recv()
		if rcvr == nil || !deref(rcvr.Type).IsFullyInstantiated() {
			base.FatalfAt(nameNode.Pos(), "Unexpected function instantiation %v with no body", nameNode)
		}
	} else {
		checkFetchBody(nameNode)
	}

	var tparams []*types.Type
	if isMeth {
		// 从方法接收器获取类型参数（跳过任何指针后）
		recvType := nameNode.Type().Recv().Type
		recvType = deref(recvType)
		if recvType.IsFullyInstantiated() {
			// 获取基本泛型类型的类型，因此我们获取
			// 其原始类型参数。
			recvType = recvType.OrigType()
		}
		tparams = recvType.RParams()
	} else {
		fields := nameNode.Type().TParams().Fields().Slice()
		tparams = make([]*types.Type, len(fields))
		for i, f := range fields {
			tparams[i] = f.Type
		}
	}

	// 将任何非形状类型的参数转换为它们的形状，这样我们就可以减少必须生成的实例化的数量。实际上，您可以混合使用形状参数和非形状参数，因为推断或显式地使用指定的具体类型参数。
	s1 := make([]*types.Type, len(shapes))
	for i, t := range shapes {
		var tparam *types.Type
		// 形状因结构类型的不同而分组，因此我们将类型参数传递给Shapify（），以便区分。
		tparam = tparams[i]
		if !t.IsShape() {
			s1[i] = typecheck.Shapify(t, i, tparam)
		} else {
			// 已经是一个形状，但请确保它有正确的索引。
			s1[i] = typecheck.Shapify(shapes[i].Underlying(), i, tparam)
		}
	}
	shapes = s1

	sym := typecheck.MakeFuncInstSym(nameNode.Sym(), shapes, false, isMeth)
	info := g.instInfoMap[sym]
	if info == nil {
		// 如果实例化还不存在，创建它并将
		// 添加到DECL列表中。
		info = &instInfo{
			dictInfo: &dictInfo{},
		}
		info.dictInfo.shapeToBound = make(map[*types.Type]*types.Type)

		if sym.Def != nil {
			// 此实例化必须是从另一个
			// 包导入的（因为内联需要它），因此我们应该
			// 不要重新生成它，并且对
			// 符号有冲突的定义（问题#50121）。它已经通过了dictPass的字典转换，所以我们实际上不需要这些信息。dictParam和info。形状绑定信息填写在
			// 下面。我们只是将导入的实例化设置为info。享乐
			assert(sym.Pkg != types.LocalPkg)
			info.fun = sym.Def.(*ir.Name).Func
			assert(info.fun != nil)
			g.instInfoMap[sym] = info
			return info
		}

		// genericSubst填写信息。dictParam和info。变形的。
		st := g.genericSubst(sym, nameNode, tparams, shapes, isMeth, info)
		info.fun = st
		g.instInfoMap[sym] = info

		// getInstInfo填写信息。dictInfo。
		g.getInstInfo(st, shapes, info)
		if base.Flag.W > 1 {
			ir.Dump(fmt.Sprintf("\nstenciled %v", st), st)
		}

		// 一切正常！
		st.SetDupok(true)
		typecheck.Target.Decls = append(typecheck.Target.Decls, st)
		g.newInsts = append(g.newInsts, st)
	}
	return info
}

// 在创建带有指定类型参数的泛型函数的
// 实例化时，包含替换所需信息的结构。
type subster struct {
	g        *genInst
	isMethod bool     // 如果一个方法正在实例化
	newf     *ir.Func // 新模板函数的Func节点
	ts       typecheck.Tsubster
	info     *instInfo // 在实例化中放置额外信息

	// 从非nil、非ONAME节点n映射到所有m的切片，其中m.Defn=n 
	defnMap map[ir.Node][]**ir.Name
}

// genericSubst返回一个名为newsym的新函数。该函数是由namedNode指定的、类型为
// args shapes的泛型函数或方法的实例化。对于具有泛型接收器的方法，它返回一个实例化的
// 函数类型，其中接收器成为第一个参数。对于一般的
// 方法或函数，添加一个dictionary参数作为第一个
// 参数。genericSubst填写信息。dictParam和info。变形的。
func (g *genInst) genericSubst(newsym *types.Sym, nameNode *ir.Name, tparams []*types.Type, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func {
	gf := nameNode.Func
	// 实例化函数的位置与泛型函数
	newf := ir.NewFunc(gf.Pos())
	newf.Pragma = gf.Pragma // 从泛型函数复制到模版实现。
	newf.Nname = ir.NewNameAt(gf.Pos(), newsym)
	newf.Nname.Func = newf
	newf.Nname.Defn = newf
	newsym.Def = newf.Nname
	savef := ir.CurFunc
	// transformCall/transformReturn（在打印正文时调用）
	// 取决于ir。设置了CurFunc。
	ir.CurFunc = newf

	assert(len(tparams) == len(shapes))

	subst := &subster{
		g:        g,
		isMethod: isMethod,
		newf:     newf,
		info:     info,
		ts: typecheck.Tsubster{
			Tparams: tparams,
			Targs:   shapes,
			Vars:    make(map[*ir.Name]*ir.Name),
		},
		defnMap: make(map[ir.Node][]**ir.Name),
	}

	newf.Dcl = make([]*ir.Name, 0, len(gf.Dcl)+1)

	// 创建所需的dictionary参数
	dictionarySym := newsym.Pkg.Lookup(typecheck.LocalDictName)
	dictionaryType := types.Types[types.TUINTPTR]
	dictionaryName := ir.NewNameAt(gf.Pos(), dictionarySym)
	typed(dictionaryType, dictionaryName)
	dictionaryName.Class = ir.PPARAM
	dictionaryName.Curfn = newf
	newf.Dcl = append(newf.Dcl, dictionaryName)
	for _, n := range gf.Dcl {
		if n.Sym().Name == typecheck.LocalDictName {
			panic("already has dictionary")
		}
		newf.Dcl = append(newf.Dcl, subst.localvar(n))
	}
	dictionaryArg := types.NewField(gf.Pos(), dictionarySym, dictionaryType)
	dictionaryArg.Nname = dictionaryName
	info.dictParam = dictionaryName

	// 我们将dictionary添加为函数签名中的第一个参数。
	// 我们还将一个方法类型转换为相应的函数类型
	// （使接收器成为字典后面的下一个参数）。
	oldt := nameNode.Type()
	var args []*types.Field
	args = append(args, dictionaryArg)
	args = append(args, oldt.Recvs().FieldSlice()...)
	args = append(args, oldt.Params().FieldSlice()...)

	// 通过subst.fields替换函数签名中的类型。
	// 丑陋：另外，我们必须将参数/结果的名称节点插入函数类型中。当前函数类型没有设置Nname字段
	// 因为它是通过从types2类型转换而来的。
	newt := types.NewSignature(oldt.Pkg(), nil, nil,
		subst.fields(ir.PPARAM, args, newf.Dcl),
		subst.fields(ir.PPARAMOUT, oldt.Results().FieldSlice(), newf.Dcl))

	typed(newt, newf.Nname)
	ir.MarkFunc(newf.Nname)
	newf.SetTypecheck(1)

	// 在替换正文之前，请确保设置了newf的名称/类型。
	newf.Body = subst.list(gf.Body)
	if len(newf.Body) == 0 {
		// 对于49524号发行版，请确保尸体非空。
		// TODO:有没有其他方法来检测
		// 一个声明了无主体的函数与一个声明了空主体的函数之间的差异？
		newf.Body = append(newf.Body, ir.NewBlockStmt(gf.Pos(), nil))
	}

	if len(subst.defnMap) > 0 {
		base.Fatalf("defnMap is not empty")
	}

	for i, tp := range tparams {
		info.dictInfo.shapeToBound[shapes[i]] = subst.ts.Typ(tp.Bound())
	}

	ir.CurFunc = savef

	return subst.newf
}

// localvar为指定的局部变量创建一个新的名称节点，并将其输入subst.vars中。它根据需要用类型参数替换
// name类型中的类型参数。
func (subst *subster) localvar(name *ir.Name) *ir.Name {
	m := ir.NewNameAt(name.Pos(), name.Sym())
	if name.IsClosureVar() {
		m.SetIsClosureVar(true)
	}
	m.SetType(subst.ts.Typ(name.Type()))
	m.BuiltinOp = name.BuiltinOp
	m.Curfn = subst.newf
	m.Class = name.Class
	assert(name.Class != ir.PEXTERN && name.Class != ir.PFUNC)
	m.Func = name.Func
	subst.ts.Vars[name] = m
	m.SetTypecheck(1)
	m.DictIndex = name.DictIndex
	if name.Defn != nil {
		if name.Defn.Op() == ir.ONAME {
			// 这是一个闭包变量，所以它的Defn是外部
			// 捕获的变量，已经被替换。
			m.Defn = subst.node(name.Defn)
		} else {
			// Defn的其他值是
			// 函数体中的节点，所以只要记住映射，我们就可以在创建新的body节点时在node（）中正确设置Defn 
			// 了。我们
			// 在替换主体之前，总是对所有局部变量调用localvar（）。
			slice := subst.defnMap[name.Defn]
			subst.defnMap[name.Defn] = append(slice, &m)
		}
	}
	if name.Outer != nil {
		m.Outer = subst.node(name.Outer).(*ir.Name)
	}

	return m
}

// getDictionaryEntry获取字典目录中的第i个条目。
func getDictionaryEntry(pos src.XPos, dict *ir.Name, i int, size int) ir.Node {
	// Convert dictionary to*[N]uintptr 
	// 字典中的所有条目都是指针。但是，它们都指向静态数据，因此我们将它们视为uintpttr，这样GC就不需要跟踪它们。
	d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], dict)
	d.SetTypecheck(1)
	d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(size)).PtrTo(), d)
	d.SetTypecheck(1)
	types.CheckSize(d.Type().Elem())

	// 从字典中加载条目i。
	deref := ir.NewStarExpr(pos, d)
	typed(d.Type().Elem(), deref)
	idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), dict) // TODO:将orig设置为什么？
	typed(types.Types[types.TUINTPTR], idx)
	r := ir.NewIndexExpr(pos, deref, idx)
	typed(types.Types[types.TUINTPTR], r)
	return r
}

// getDictionaryType返回*运行时_来自字典条目i的类型（
// 指的是类型参数或使用类型参数的派生类型）。它使用
// 指定的dictionary dictParam，而不是info中的dictionary dictParam。dictParam。
func getDictionaryType(info *instInfo, dictParam *ir.Name, pos src.XPos, i int) ir.Node {
	if i < 0 || i >= info.dictInfo.startSubDict {
		base.Fatalf(fmt.Sprintf("bad dict index %d", i))
	}

	r := getDictionaryEntry(pos, dictParam, i, info.dictInfo.startSubDict)
	// 将检索到的字典条目的类型更改为*字节，这是
	// 标准的*运行时类型_在编译器中键入
	typed(types.Types[types.TUINT8].PtrTo(), r)
	return r
}

// 节点类似于DeepCopy（），但基于subst.ts.vars和
// 也会降为闭包。它用类型参数替换
// 所有新节点中的类型参数，并执行在通用
// 函数上延迟的转换。
func (subst *subster) node(n ir.Node) ir.Node {
	// 使用闭包捕获ir所需的所有状态。孩子们在争论。
	var edit func(ir.Node) ir.Node
	edit = func(x ir.Node) ir.Node {
		// 类似于ir。在类型检查开始时设置pos（）。typecheck（）-
		// 允许使用base。转换函数期间的Pos，就像
		// tc*（）函数一样。
		ir.SetPos(x)
		switch x.Op() {
		case ir.OTYPE:
			return ir.TypeNode(subst.ts.Typ(x.Type()))

		case ir.ONAME:
			if v := subst.ts.Vars[x.(*ir.Name)]; v != nil {
				return v
			}
			if ir.IsBlank(x) {
				// 特殊情况，因为一个空白的局部变量是
				// 不在fn中。Dcl列表。
				m := ir.NewNameAt(x.Pos(), ir.BlankNode.Sym())
				return typed(subst.ts.Typ(x.Type()), m)
			}
			return x
		case ir.ONONAME:
			// 这将处理类型开关保护中的标识符
			fallthrough
		case ir.OLITERAL, ir.ONIL:
			if x.Sym() != nil {
				return x
			}
		}
		m := ir.Copy(x)

		slice, ok := subst.defnMap[x]
		if ok {
			// 我们刚刚复制了一个非ONAME节点，它是局部变量的Defn值
			// 。将复制的
			// 局部变量的Defn值设置为此新的Defn节点。
			for _, ptr := range slice {
				(*ptr).Defn = m
			}
			delete(subst.defnMap, x)
		}

		if _, isExpr := m.(ir.Expr); isExpr {
			t := x.Type()
			if t == nil {
				// 检查t可以为零的已知情况（调用
				// 没有返回值和键表达式）
				// 否则会导致致命错误。
				_, isCallExpr := m.(*ir.CallExpr)
				_, isStructKeyExpr := m.(*ir.StructKeyExpr)
				_, isKeyExpr := m.(*ir.KeyExpr)
				if !isCallExpr && !isStructKeyExpr && !isKeyExpr && x.Op() != ir.OPANIC &&
					x.Op() != ir.OCLOSE {
					base.FatalfAt(m.Pos(), "Nil type for %v", x)
				}
			} else if x.Op() != ir.OCLOSURE {
				m.SetType(subst.ts.Typ(x.Type()))
			}
		}

		ir.EditChildren(m, edit)

		m.SetTypecheck(1)

		// 执行我们在泛型函数
		// 节点上延迟的转换，现在我们已经替换了类型args。
		switch x.Op() {
		case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
			transformCompare(m.(*ir.BinaryExpr))

		case ir.OSLICE, ir.OSLICE3:
			transformSlice(m.(*ir.SliceExpr))

		case ir.OADD:
			m = transformAdd(m.(*ir.BinaryExpr))

		case ir.OINDEX:
			transformIndex(m.(*ir.IndexExpr))

		case ir.OAS2:
			as2 := m.(*ir.AssignListStmt)
			transformAssign(as2, as2.Lhs, as2.Rhs)

		case ir.OAS:
			as := m.(*ir.AssignStmt)
			if as.Y != nil {
				// transformAssign不处理dcl归零赋值的情况
				// （rhs[0]为零）。
				lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y}
				transformAssign(as, lhs, rhs)
				as.X, as.Y = lhs[0], rhs[0]
			}

		case ir.OASOP:
			as := m.(*ir.AssignOpStmt)
			transformCheckAssign(as, as.X)

		case ir.ORETURN:
			transformReturn(m.(*ir.ReturnStmt))

		case ir.OSEND:
			transformSend(m.(*ir.SendStmt))

		case ir.OSELECT:
			transformSelect(m.(*ir.SelectStmt))

		case ir.OCOMPLIT:
			transformCompLit(m.(*ir.CompLitExpr))

		case ir.OADDR:
			transformAddr(m.(*ir.AddrExpr))

		case ir.OLITERAL:
			t := m.Type()
			if t != x.Type() {
				// types2将为我们提供一个类型为T的常量，如果一个非类型常量与另一个类型为T的
				// 操作数一起使用（以可证明正确的方式）。
				// 当我们在
				// 制版过程中替换类型args时，我们现在知道了
				// 常量的真实类型。然后我们可能需要更改
				// BasicLit。val是正确的类型（例如
				// 将int64Val常量转换为floatVal 
				// 常量）。
				m.SetType(types.UntypedInt) // 使用DefaultLit的任何非类型类型来工作
				m = typecheck.DefaultLit(m, t)
			}

		case ir.OXDOT:
			// 完成OXDOT的转换，除非这是
			// 对类型参数的绑定调用或字段访问。
			// 绑定调用或类型参数上的字段访问将在
			// dictPass期间转换。否则，m将被转换为
			// OMETHVALUE节点。如果我们在下面的OCALL案例中发现实际调用了
			// odottinter节点。
			// 方法值，那么它将被转换为一个ODOTMETH或
			mse := m.(*ir.SelectorExpr)
			if src := mse.X.Type(); !src.IsShape() {
				transformDot(mse, false)
			}

		case ir.OCALL:
			call := m.(*ir.CallExpr)
			switch call.X.Op() {
			case ir.OTYPE:
				// 转换，现在我们知道了
				// 类型参数。
				m = transformConvCall(call)

			case ir.OMETHVALUE, ir.OMETHEXPR:
				// 现在我们知道正在调用方法值，请重新进行OXDOT的转换。然后
				// 转换调用。
				call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
				transformDot(call.X.(*ir.SelectorExpr), true)
				transformCall(call)

			case ir.ODOT, ir.ODOTPTR:
				// 通用接收器的OXDOT解析为
				// 对具有函数
				// 值的字段的访问。将调用转换为该函数，现在
				// 表示OXDOT已被解析。
				transformCall(call)

			case ir.ONAME:
				name := call.X.Name()
				if name.BuiltinOp != ir.OXXX {
					m = transformBuiltin(call)
				} else {
					// 这是一个函数值的情况，它是一个
					// 类型参数（通过
					// 结构约束暗示为一个函数），现在已解决。
					transformCall(call)
				}

			case ir.OFUNCINST:
				// 一旦我们创建并附加了要调用的
				// 实例化，带有OFUNINST的调用将在stencil（）中进行转换。
				// 我们现在必须转换调用的参数，尽管如此，
				// 以便暴露任何需要的confiface节点，
				// 以便字典格式正确。
				transformEarlyCall(call)

			case ir.OXDOT:
				// 这是对typeparam的绑定调用或字段访问
				// 的情况，将在
				// dictPass中处理。与OfUnnst一样，我们现在必须转换调用的
				// 参数，以便暴露任何需要的方便
				// 节点。
				transformEarlyCall(call)

			case ir.ODOTTYPE, ir.ODOTTYPE2:
				// 这些点类型可以通过dict传递转换为
				// ODYNAMIC点类型。

			default:
				// 将调用转换为
				// 调用的所有其他值。不需要任何特殊
				// 处理的X.Op（）。
				transformCall(call)

			}

		case ir.OCLOSURE:
			// 我们将从头开始创建一个新的闭包，因此请清除m 
			// 以避免使用ir。意外复制，直到我们重新分配它。
			m = nil

			x := x.(*ir.ClosureExpr)
			// 需要复制x.Func。n名字，x.Func。Dcl，x.Func。ClosureVars和
			// x.Func。身体
			oldfn := x.Func
			newfn := ir.NewClosureFunc(oldfn.Pos(), subst.newf != nil)
			ir.NameClosure(newfn.OClosure, subst.newf)

			saveNewf := subst.newf
			ir.CurFunc = newfn
			subst.newf = newfn
			newfn.Dcl = subst.namelist(oldfn.Dcl)

			// 为包含函数的
			// 的字典创建一个闭包变量。
			cdict := ir.CaptureName(oldfn.Pos(), newfn, subst.info.dictParam)
			typed(types.Types[types.TUINTPTR], cdict)
			ir.FinishCaptureNames(oldfn.Pos(), saveNewf, newfn)
			newfn.ClosureVars = append(newfn.ClosureVars, subst.namelist(oldfn.ClosureVars)...)

			// 将该闭包变量复制到本地变量。
			// 注意：这允许子闭包捕获字典。
			// 见第47723期。
			ldict := ir.NewNameAt(x.Pos(), newfn.Sym().Pkg.Lookup(typecheck.LocalDictName))
			typed(types.Types[types.TUINTPTR], ldict)
			ldict.Class = ir.PAUTO
			ldict.Curfn = newfn
			newfn.Dcl = append(newfn.Dcl, ldict)
			as := ir.NewAssignStmt(x.Pos(), ldict, cdict)
			as.SetTypecheck(1)
			ldict.Defn = as
			newfn.Body.Append(as)

			// 为实例化的闭包创建inst info。格言
			// param是
			// 外部函数的字典的闭包变量。因为字典是共享的，所以使用
			// 相同的dictInfo。
			cinfo := &instInfo{
				fun:       newfn,
				dictParam: ldict,
				dictInfo:  subst.info.dictInfo,
			}
			subst.g.instInfoMap[newfn.Nname.Sym()] = cinfo

			typed(subst.ts.Typ(oldfn.Nname.Type()), newfn.Nname)
			typed(newfn.Nname.Type(), newfn.OClosure)
			newfn.SetTypecheck(1)

			outerinfo := subst.info
			subst.info = cinfo
			// 在执行body之前，请确保设置了闭包函数的类型。
			newfn.Body.Append(subst.list(oldfn.Body)...)
			subst.info = outerinfo
			subst.newf = saveNewf
			ir.CurFunc = saveNewf

			m = ir.UseClosure(newfn.OClosure, typecheck.Target)
			subst.g.newInsts = append(subst.g.newInsts, m.(*ir.ClosureExpr).Func)
			m.(*ir.ClosureExpr).SetInit(subst.list(x.Init()))

		case ir.OSWITCH:
			m := m.(*ir.SwitchStmt)
			if m.Tag != nil && m.Tag.Op() == ir.OTYPESW {
				break // 这里没有关于类型开关的操作。
			}
			if m.Tag != nil && !m.Tag.Type().IsInterface() && m.Tag.Type().HasShape() {
				// 要在类型参数的值上实现切换，我们首先将要切换的对象转换为接口{}。
				m.Tag = assignconvfn(m.Tag, types.Types[types.TINTER])
			}
			for _, c := range m.Cases {
				for i, x := range c.List {
					// 如果我们有一个是类型参数或具有类型参数的case，请将该case 
					// 转换为接口{}。
					if !x.Type().IsInterface() && x.Type().HasShape() {
						c.List[i] = assignconvfn(x, types.Types[types.TINTER])
					}
				}
			}

		}
		return m
	}

	return edit(n)
}

// dictPass进行函数实例化，并在
// 上执行需要使用dictionary参数的操作。
func (g *genInst) dictPass(info *instInfo) {
	savef := ir.CurFunc
	ir.CurFunc = info.fun

	var edit func(ir.Node) ir.Node
	edit = func(m ir.Node) ir.Node {
		ir.EditChildren(m, edit)

		switch m.Op() {
		case ir.OCLOSURE:
			newf := m.(*ir.ClosureExpr).Func
			ir.CurFunc = newf
			outerinfo := info
			info = g.instInfoMap[newf.Nname.Sym()]

			body := newf.Body
			for i, n := range body {
				body[i] = edit(n)
			}

			info = outerinfo
			ir.CurFunc = info.fun

		case ir.OXDOT:
			// 这是类型参数上的点访问的情况。这是
			// 通常是对类型param的绑定调用，但如果约束具有单一结构类型，则可以是
			// 字段访问。
			mse := m.(*ir.SelectorExpr)
			src := mse.X.Type()
			assert(src.IsShape())

			if mse.X.Op() == ir.OTYPE {
				// 方法表达式T.M 
				m = g.buildClosure2(info, m)
				// 不需要transformDot-buildClosure2已经
				// 转换为Ocaliner/ODOTINTER。
			} else {
				// 如果我们在
				// 绑定的所有方法中找不到所选方法，那么这必须是对结构类型字段的访问权限
				// 。如果是这样，我们跳过
				// 字典查找-transformDot（）将转换为
				// 所需的直接字段访问。
				if isBoundMethod(info.dictInfo, mse) {
					dst := info.dictInfo.shapeToBound[mse.X.Type()]
					// 将x.M实现为绑定接口的转换
					// 1）将x转换为绑定接口
					// 2）在该接口上调用M 
					if src.IsInterface() {
						// 如果类型arg是接口（异常情况），
						// 我们对绑定的类型进行类型断言。
						mse.X = assertToBound(info, info.dictParam, m.Pos(), mse.X, dst)
					} else {
						mse.X = convertUsingDictionary(info, info.dictParam, m.Pos(), mse.X, m, dst, true)
						// 注意：我们设置了nonEscaping==true，因为我们可以假设
						// 接口转换的备份存储没有转义。方法调用将立即转到
						// 一个包装函数，它将所有数据从接口值中复制出来。
						// /（它只对非指针形状的接口转换有用。请参阅第50182期。）
					}
				}
				transformDot(mse, false)
			}
		case ir.OCALL:
			call := m.(*ir.CallExpr)
			op := call.X.Op()
			if op == ir.OMETHVALUE {
				// 现在我们知道正在调用方法值，所以重新进行OXDOT的转换。
				call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
				transformDot(call.X.(*ir.SelectorExpr), true)
			}
			transformCall(call)

		case ir.OCONVIFACE:
			if m.Type().IsEmptyInterface() && m.(*ir.ConvExpr).X.Type().IsEmptyInterface() {
				// Was T->interface{}，在模版打印后现在是interface{}->interface{}。
				// 不再需要转换。见第48276期。
				m.(*ir.ConvExpr).SetOp(ir.OCONVNOP)
				break
			}
			mce := m.(*ir.ConvExpr)
			// 注意：x的参数仍然是类型参数。
			// m的参数现在有一个实例化类型。
			if mce.X.Type().HasShape() || (mce.X.Type().IsInterface() && m.Type().HasShape()) {
				m = convertUsingDictionary(info, info.dictParam, m.Pos(), m.(*ir.ConvExpr).X, m, m.Type(), false)
			}
		case ir.ODOTTYPE, ir.ODOTTYPE2:
			if !m.Type().HasShape() {
				break
			}
			dt := m.(*ir.TypeAssertExpr)
			var rt ir.Node
			if dt.Type().IsInterface() || dt.X.Type().IsEmptyInterface() {
				ix := findDictType(info, m.Type())
				assert(ix >= 0)
				rt = getDictionaryType(info, info.dictParam, dt.Pos(), ix)
			} else {
				// 非空接口到非接口。需要itab。
				ix := -1
				for i, ic := range info.dictInfo.itabConvs {
					if ic == m {
						ix = info.dictInfo.startItabConv + i
						break
					}
				}
				assert(ix >= 0)
				rt = getDictionaryEntry(dt.Pos(), info.dictParam, ix, info.dictInfo.dictLen)
			}
			op := ir.ODYNAMICDOTTYPE
			if m.Op() == ir.ODOTTYPE2 {
				op = ir.ODYNAMICDOTTYPE2
			}
			m = ir.NewDynamicTypeAssertExpr(dt.Pos(), op, dt.X, rt)
			m.SetType(dt.Type())
			m.SetTypecheck(1)
		case ir.OCASE:
			if _, ok := m.(*ir.CommClause); ok {
				// 这不是类型开关。TODO:我们应该在这里使用OSWITCH而不是OCASE吗？
				break
			}
			m := m.(*ir.CaseClause)
			for i, c := range m.List {
				if c.Op() == ir.OTYPE && c.Type().HasShape() {
					// 使用*运行时_类型为动态类型。
					ix := findDictType(info, m.List[i].Type())
					assert(ix >= 0)
					dt := ir.NewDynamicType(c.Pos(), getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen))

					// 对于从非空接口到非接口的类型切换，我们还需要一个itab。
					if !m.List[i].Type().IsInterface() {
						if _, ok := info.dictInfo.type2switchType[m.List[i]]; ok {
							// 从非空接口输入开关。我们需要一个运行时间。itab 
							// 用于动态类型。
							ix := -1
							for j, ic := range info.dictInfo.itabConvs {
								if ic == m.List[i] {
									ix = info.dictInfo.startItabConv + j
									break
								}
							}
							assert(ix >= 0)
							dt.ITab = getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen)
						}
					}
					typed(m.List[i].Type(), dt)
					m.List[i] = dt
				}
			}

		}
		return m
	}
	edit(info.fun)
	ir.CurFunc = savef
}

// findDictType在typeparams中查找类型t，或在泛型
// 函数信息中查找派生类型。gfInfo。这将指示带有
// 的字典条目，该条目对应于相关实例化函数的正确具体类型。
func findDictType(info *instInfo, t *types.Type) int {
	for i, dt := range info.dictInfo.shapeParams {
		if dt == t {
			return i
		}
	}
	for i, dt := range info.dictInfo.derivedTypes {
		if types.IdenticalStrict(dt, t) {
			return i + len(info.dictInfo.shapeParams)
		}
	}
	return -1
}

// convertUsingDictionary通过返回一组使用字典条目的新节点，将实例化的值v（type v.type（））转换为接口
// type dst。in是导致
// 转换的Confiface节点或XDOT节点（用于绑定方法调用）的实例化节点。
// 如果nonEscaping为true，则调用方保证接口数据
// word所需的备份存储不会转义。
func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, in ir.Node, dst *types.Type, nonEscaping bool) ir.Node {
	assert(v.Type().HasShape() || v.Type().IsInterface() && in.Type().HasShape())
	assert(dst.IsInterface())

	if v.Type().IsInterface() {
		// 从接口转换。源代码的形状并不重要，因为我们将使用第一个接口单词中的具体类型。
		if dst.IsEmptyInterface() {
			// 转换I2E。Oconvide为我们做到了这一点，它不依赖于
			// 空接口是用什么实例化的。不需要词典条目。
			v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v)
			v.SetTypecheck(1)
			return v
		}
		if !in.Type().HasShape() {
			// 如果目的地未参数化，则常规OConvide有效。
			v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v)
			v.SetTypecheck(1)
			return v
		}

		// 我们从字典中获取目标接口类型，从参数的itab中获取具体的
		// 类型。调用运行时。说服我得到新的itab。
		tmp := typecheck.Temp(v.Type())
		as := ir.NewAssignStmt(pos, tmp, v)
		as.SetTypecheck(1)
		itab := ir.NewUnaryExpr(pos, ir.OITAB, tmp)
		typed(types.Types[types.TUINTPTR].PtrTo(), itab)
		idata := ir.NewUnaryExpr(pos, ir.OIDATA, tmp)
		typed(types.Types[types.TUNSAFEPTR], idata)

		fn := typecheck.LookupRuntime("convI2I")
		fn.SetTypecheck(1)
		types.CalcSize(fn.Type())
		call := ir.NewCallExpr(pos, ir.OCALLFUNC, fn, nil)
		typed(types.Types[types.TUINT8].PtrTo(), call)
		ix := findDictType(info, in.Type())
		assert(ix >= 0)
		inter := getDictionaryType(info, dictParam, pos, ix)
		call.Args = []ir.Node{inter, itab}
		i := ir.NewBinaryExpr(pos, ir.OEFACE, call, idata)
		typed(dst, i)
		i.PtrInit().Append(as)
		return i
	}

	var rt ir.Node
	if !dst.IsEmptyInterface() {
		// 我们应该在字典里有一个itab条目。使用这个itab 
		// 将比先转换为空接口
		// 然后向dst输入断言更有效。
		ix := -1
		for i, ic := range info.dictInfo.itabConvs {
			if ic == in {
				ix = info.dictInfo.startItabConv + i
				break
			}
		}
		assert(ix >= 0)
		rt = getDictionaryEntry(pos, dictParam, ix, info.dictInfo.dictLen)
	} else {
		ix := findDictType(info, v.Type())
		assert(ix >= 0)
		// 加载实际运行时_字典中类型参数的类型。
		rt = getDictionaryType(info, dictParam, pos, ix)
	}

	// 找出接口的数据字段。
	data := ir.NewConvExpr(pos, ir.OCONVIDATA, nil, v)
	typed(types.Types[types.TUNSAFEPTR], data)
	data.NonEscaping = nonEscaping

	// 从类型和数据部分构建一个接口。
	var i ir.Node = ir.NewBinaryExpr(pos, ir.OEFACE, rt, data)
	typed(dst, i)
	return i
}

func (subst *subster) namelist(l []*ir.Name) []*ir.Name {
	s := make([]*ir.Name, len(l))
	for i, n := range l {
		s[i] = subst.localvar(n)
	}
	return s
}

func (subst *subster) list(l []ir.Node) []ir.Node {
	s := make([]ir.Node, len(l))
	for i, n := range l {
		s[i] = subst.node(n)
	}
	return s
}

// fields根据dcl中相应的输入/输出参数，为类型签名中的字段节点设置Nname字段。这取决于输入和输出
// 参数在dcl中的顺序。
func (subst *subster) fields(class ir.Class, oldfields []*types.Field, dcl []*ir.Name) []*types.Field {
	// 在类的声明的dcl中查找起始索引（
	// PPARAM或PPARAMOUT）。
	var i int
	for i = range dcl {
		if dcl[i].Class == class {
			break
		}
	}

	// 创建新字段节点，这些节点是oldfields节点的副本，但
	// 替换任何类型参数，并将Nname设置为
	// Dcl中对应PPARAM或PPARAMOUT的节点。
	newfields := make([]*types.Field, len(oldfields))
	for j := range oldfields {
		newfields[j] = oldfields[j].Copy()
		newfields[j].Type = subst.ts.Typ(oldfields[j].Type)
		// 如果名称为
		// 未指定或指定为“173”，则dcl中将缺少一个PPARAM字段。因此，我们将dcl sym 
		// 与字段sym（或字段的Nname节点的sym）进行比较。（未命名的
		// 结果的Nname节点中仍有类似于~r2的名称。）如果
		// 它们不匹配，则此dcl（如果还有一个）必须应用于
		// 后面的字段。
		if i < len(dcl) && (dcl[i].Sym() == oldfields[j].Sym ||
			(oldfields[j].Nname != nil && dcl[i].Sym() == oldfields[j].Nname.Sym())) {
			newfields[j].Nname = dcl[i]
			i++
		}
	}
	return newfields
}

// 如果是指针类型，deref只执行一个t类型的deref。
func deref(t *types.Type) *types.Type {
	if t.IsPtr() {
		return t.Elem()
	}
	return t
}

// markTypeUsed将t型标记为所用，以帮助避免消除
// 所需方法的死代码。
func markTypeUsed(t *types.Type, lsym *obj.LSym) {
	if t.IsInterface() {
		return
	}
	// TODO:这有点过分了，我们真的只需要它
	// 用于放入接口的类型。
	// 注意：此重新定位也用于cmd/link/internal/ld/dwarf。go 
	reflectdata.MarkTypeUsedInInterface(t, lsym)
}

// getDictionarySym返回命名泛型函数gf的字典，该函数用类型参数targs实例化。
func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) *types.Sym {
	if len(targs) == 0 {
		base.Fatalf("%s should have type arguments", gf.Sym().Name)
	}

	// 强制执行只有具体类型才能到达此处。
	for _, t := range targs {
		if t.HasShape() {
			panic(fmt.Sprintf("shape %+v in dictionary for %s", t, gf.Sym().Name))
		}
	}

	// 获取代表字典的符号。
	sym := typecheck.MakeDictSym(gf.Sym(), targs, isMeth)

	// 初始化字典，如果我们还没有初始化的话。
	lsym := sym.Linksym()
	if len(lsym.P) > 0 {
		// 我们已经开始创建这本词典及其lsym。
		return sym
	}

	infoPrint("=== Creating dictionary %v\n", sym.Name)
	off := 0
	// 为每个targ（混凝土类型或gcshape）发出一个条目。
	for _, t := range targs {
		infoPrint(" * %v\n", t)
		s := reflectdata.TypeLinksym(t)
		off = objw.SymPtr(lsym, off, s, 0)
		markTypeUsed(t, lsym)
	}

	instInfo := g.getInstantiation(gf, targs, isMeth)
	info := instInfo.dictInfo

	subst := typecheck.Tsubster{
		Tparams: info.shapeParams,
		Targs:   targs,
	}
	// 为每个派生类型发出一个条目（替换targs后）
	for _, t := range info.derivedTypes {
		ts := subst.Typ(t)
		infoPrint(" - %v\n", ts)
		s := reflectdata.TypeLinksym(ts)
		off = objw.SymPtr(lsym, off, s, 0)
		markTypeUsed(ts, lsym)
	}
	// 为每个子字典发出一个条目（替换targs后）
	for _, subDictInfo := range info.subDictCalls {
		var sym *types.Sym
		n := subDictInfo.callNode
		switch n.Op() {
		case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH:
			call := n.(*ir.CallExpr)
			if call.X.Op() == ir.OXDOT || call.X.Op() == ir.ODOTMETH {
				var nameNode *ir.Name
				se := call.X.(*ir.SelectorExpr)
				if se.X.Type().IsShape() {
					// 这是一个由类型绑定启用的方法调用。

					// 我们需要对方法表达式进行额外检查，
					// 这些表达式没有添加到隐式XDOT中。
					tmpse := ir.NewSelectorExpr(src.NoXPos, ir.OXDOT, se.X, se.Sel)
					tmpse = typecheck.AddImplicitDots(tmpse)
					tparam := tmpse.X.Type()
					if !tparam.IsShape() {
						// 方法表达式在typeparam上不是
						// 真的。
						break
					}
					ix := -1
					for i, shape := range info.shapeParams {
						if shape == tparam {
							ix = i
							break
						}
					}
					assert(ix >= 0)
					recvType := targs[ix]
					if recvType.IsInterface() || len(recvType.RParams()) == 0 {
						// 实际上不需要子字典条目
						// 因为
						// 类型arg不是
						// 
						// 将具有泛型方法的实例化类型。
						break
					}
					// 这是对
					// 实例化类型的方法调用，所以我们需要一个
					// 子字典。
					targs := recvType.RParams()
					genRecvType := recvType.OrigType()
					nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
					sym = g.getDictionarySym(nameNode, targs, true)
				} else {
					// 这是对泛型类型的正常
					// 方法调用的情况。
					assert(subDictInfo.savedXNode == se)
					sym = g.getSymForMethodCall(se, &subst)
				}
			} else {
				inst, ok := call.X.(*ir.InstExpr)
				if ok {
					// 代码尚未转换
					assert(subDictInfo.savedXNode == inst)
				}
				// 如果！好的，那么泛型方法/函数调用
				// 已经被转换为形状实例化
				// 调用。无论哪种方式，都可以使用信息中保存的SelectorExpr/InstExpr 
				// 节点。
				cex := subDictInfo.savedXNode
				if se, ok := cex.(*ir.SelectorExpr); ok {
					sym = g.getSymForMethodCall(se, &subst)
				} else {
					inst := cex.(*ir.InstExpr)
					nameNode := inst.X.(*ir.Name)
					subtargs := typecheck.TypesOf(inst.Targs)
					for i, t := range subtargs {
						subtargs[i] = subst.Typ(t)
					}
					sym = g.getDictionarySym(nameNode, subtargs, false)
				}
			}

		case ir.OFUNCINST:
			inst := n.(*ir.InstExpr)
			nameNode := inst.X.(*ir.Name)
			subtargs := typecheck.TypesOf(inst.Targs)
			for i, t := range subtargs {
				subtargs[i] = subst.Typ(t)
			}
			sym = g.getDictionarySym(nameNode, subtargs, false)

		case ir.OXDOT, ir.OMETHEXPR, ir.OMETHVALUE:
			sym = g.getSymForMethodCall(n.(*ir.SelectorExpr), &subst)

		default:
			assert(false)
		}

		if sym == nil {
			// 未使用的子字典条目，仅发出0。
			off = objw.Uintptr(lsym, off, 0)
			infoPrint(" - Unused subdict entry\n")
		} else {
			off = objw.SymPtr(lsym, off, sym.Linksym(), 0)
			infoPrint(" - Subdict %v\n", sym.Name)
		}
	}

	g.instantiateMethods()
	delay := &delayInfo{
		gf:     gf,
		targs:  targs,
		sym:    sym,
		off:    off,
		isMeth: isMeth,
	}
	g.dictSymsToFinalize = append(g.dictSymsToFinalize, delay)
	return sym
}

// getSymForMethodCall获取具有选择器se的方法调用、方法值或方法
// 表达式的字典符号。
func (g *genInst) getSymForMethodCall(se *ir.SelectorExpr, subst *typecheck.Tsubster) *types.Sym {
	// 对于除方法表达式之外的所有表达式，'recvType=deref（se.X.Type）'也将给出接收方类型。对于嵌入类型的方法表达式，我们需要查看选择的类型，以获得最终的接收器类型。
	recvType := deref(se.Selection.Type.Recv().Type)
	genRecvType := recvType.OrigType()
	nameNode := typecheck.Lookdot1(se, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
	subtargs := recvType.RParams()
	s2targs := make([]*types.Type, len(subtargs))
	for i, t := range subtargs {
		s2targs[i] = subst.Typ(t)
	}
	return g.getDictionarySym(nameNode, s2targs, true)
}

// finalizeSyms在g.dictSymsToFinalize上完成所有词典，写出
// ITAB所需的任何LSyms。itab lsyms创建的包装器需要各种
// 字典和方法实例化才能完成，因此，为了避免递归的
// 依赖关系，我们只有在创建了所有字典syms和
// 实例化之后才能最终确定itab lsyms。
func (g *genInst) finalizeSyms() {
	for _, d := range g.dictSymsToFinalize {
		infoPrint("=== Finalizing dictionary %s\n", d.sym.Name)

		lsym := d.sym.Linksym()
		instInfo := g.getInstantiation(d.gf, d.targs, d.isMeth)
		info := instInfo.dictInfo

		subst := typecheck.Tsubster{
			Tparams: info.shapeParams,
			Targs:   d.targs,
		}

		// 为每个itab发出一个条目
		for _, n := range info.itabConvs {
			var srctype, dsttype *types.Type
			switch n.Op() {
			case ir.OXDOT, ir.OMETHVALUE:
				se := n.(*ir.SelectorExpr)
				srctype = subst.Typ(se.X.Type())
				dsttype = subst.Typ(info.shapeToBound[se.X.Type()])
			case ir.ODOTTYPE, ir.ODOTTYPE2:
				srctype = subst.Typ(n.(*ir.TypeAssertExpr).Type())
				dsttype = subst.Typ(n.(*ir.TypeAssertExpr).X.Type())
			case ir.OCONVIFACE:
				srctype = subst.Typ(n.(*ir.ConvExpr).X.Type())
				dsttype = subst.Typ(n.Type())
			case ir.OTYPE:
				srctype = subst.Typ(n.Type())
				dsttype = subst.Typ(info.type2switchType[n])
			default:
				base.Fatalf("itab entry with unknown op %s", n.Op())
			}
			if srctype.IsInterface() || dsttype.IsEmptyInterface() {
				// 如果src类型是接口，则不需要itab。我们将改用类型断言。
				d.off = objw.Uintptr(lsym, d.off, 0)
				infoPrint(" + Unused itab entry for %v\n", srctype)
			} else {
				// 确保所有新的完全实例化类型在生成任何ITAB之前都创建了它们的方法。
				g.instantiateMethods()
				itabLsym := reflectdata.ITabLsym(srctype, dsttype)
				d.off = objw.SymPtr(lsym, d.off, itabLsym, 0)
				infoPrint(" + Itab for (%v,%v)\n", srctype, dsttype)
			}
		}

		objw.Global(lsym, int32(d.off), obj.DUPOK|obj.RODATA)
		infoPrint("=== Finalized dictionary %s\n", d.sym.Name)
	}
	g.dictSymsToFinalize = nil
}

func (g *genInst) getDictionaryValue(pos src.XPos, gf *ir.Name, targs []*types.Type, isMeth bool) ir.Node {
	sym := g.getDictionarySym(gf, targs, isMeth)

	// 创建（或重用）引用字典符号的节点。
	var n *ir.Name
	if sym.Def != nil {
		n = sym.Def.(*ir.Name)
	} else {
		// 我们将静态字典的位置设置为
		// 它的一个用途。
		n = ir.NewNameAt(pos, sym)
		n.Curfn = ir.CurFunc
		n.SetType(types.Types[types.TUINTPTR]) // 应该是[…]uintptr，但实际上并不重要
		n.SetTypecheck(1)
		n.Class = ir.PEXTERN
		sym.Def = n
	}

	// 返回字典的地址。Addr节点获取传入的位置。
	np := typecheck.NodAddrAt(pos, n)
	// 注意：将字典指针视为uintpttr，因此它们不是关于GC的指针。这节省了堆栈扫描工作、写入障碍等。
	// 我们可以摆脱它，因为字典是全局变量。
	// TODO:使用强制转换，还是直接键入可以？
	np.SetType(types.Types[types.TUINTPTR])
	np.SetTypecheck(1)
	return np
}

// 如果targs中任何节点的类型具有形状，hasShapeNodes将返回true。
func hasShapeNodes(targs []ir.Node) bool {
	for _, n := range targs {
		if n.Type().HasShape() {
			return true
		}
	}
	return false
}

// 如果targs中的任何类型具有形状，hasShapeTypes将返回true。
func hasShapeTypes(targs []*types.Type) bool {
	for _, t := range targs {
		if t.HasShape() {
			return true
		}
	}
	return false
}

// getInstInfo获取函数实例化的字典格式——类型参数、派生的
// 类型，以及所需的子字典和ITAB。
func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInfo) {
	info := instInfo.dictInfo
	info.shapeParams = shapes

	for _, t := range info.shapeParams {
		b := info.shapeToBound[t]
		if b.HasShape() {
			// 如果类型绑定是参数化的（异常情况），那么我们
			// 在对作为接口的类型arg执行
			// 绑定调用时，可能需要其派生类型来执行类型断言。
			addType(info, nil, b)
		}
	}

	for _, n := range st.Dcl {
		addType(info, n, n.Type())
		n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1)
	}

	if infoPrintMode {
		fmt.Printf(">>> InstInfo for %v\n", st)
		for _, t := range info.shapeParams {
			fmt.Printf("  Typeparam %v\n", t)
		}
	}

	// Map记住当我们在调用中看到实例化的函数值或方法
	// 表达式/值时，我们可以确定何时遇到
	// 未调用的函数值或方法表达式/值。
	callMap := make(map[ir.Node]bool)

	var visitFunc func(ir.Node)
	visitFunc = func(n ir.Node) {
		switch n.Op() {
		case ir.OFUNCINST:
			if !callMap[n] && hasShapeNodes(n.(*ir.InstExpr).Targs) {
				infoPrint("  Closure&subdictionary required at generic function value %v\n", n.(*ir.InstExpr).X)
				info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
			}
		case ir.OMETHEXPR, ir.OMETHVALUE:
			if !callMap[n] && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) &&
				len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 &&
				hasShapeTypes(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) {
				if n.(*ir.SelectorExpr).X.Op() == ir.OTYPE {
					infoPrint("  Closure&subdictionary required at generic meth expr %v\n", n)
				} else {
					infoPrint("  Closure&subdictionary required at generic meth value %v\n", n)
				}
				info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
			}
		case ir.OCALL:
			ce := n.(*ir.CallExpr)
			if ce.X.Op() == ir.OFUNCINST {
				callMap[ce.X] = true
				if hasShapeNodes(ce.X.(*ir.InstExpr).Targs) {
					infoPrint("  Subdictionary at generic function/method call: %v - %v\n", ce.X.(*ir.InstExpr).X, n)
					// 为函数调用保存instExpr节点，
					// 因为在形状实例化中将
					// 通用函数调用转换为调用
					// 时，我们将丢失此信息。
					info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: ce.X})
				}
			}
			// 注意：只要我们继续禁用
			// 类型为
			// 声明（#45639）的RHS上的类型参数，实际上就不需要这个XDOT代码。
			if ce.X.Op() == ir.OXDOT {
				callMap[ce.X] = true
				if isBoundMethod(info, ce.X.(*ir.SelectorExpr)) {
					infoPrint("  Optional subdictionary at generic bound call: %v\n", n)
					info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: nil})
				}
			}
		case ir.OCALLMETH:
			ce := n.(*ir.CallExpr)
			if ce.X.Op() == ir.ODOTMETH &&
				len(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) > 0 {
				callMap[ce.X] = true
				if hasShapeTypes(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) {
					infoPrint("  Subdictionary at generic method call: %v\n", n)
					// 保存方法调用的选择器，因为当
					// 最终将丢失此信息。
					// 通用方法调用转换为方法形状实例化的
					// 函数调用时，我们
					info.subDictCalls = append(info.subDictCalls, subDictInfo{callNode: n, savedXNode: ce.X})
				}
			}
		case ir.OCONVIFACE:
			if n.Type().IsInterface() && !n.Type().IsEmptyInterface() &&
				n.(*ir.ConvExpr).X.Type().HasShape() {
				infoPrint("  Itab for interface conv: %v\n", n)
				info.itabConvs = append(info.itabConvs, n)
			}
		case ir.OXDOT:
			se := n.(*ir.SelectorExpr)
			if isBoundMethod(info, se) {
				infoPrint("  Itab for bound call: %v\n", n)
				info.itabConvs = append(info.itabConvs, n)
			}
		case ir.ODOTTYPE, ir.ODOTTYPE2:
			if !n.(*ir.TypeAssertExpr).Type().IsInterface() && !n.(*ir.TypeAssertExpr).X.Type().IsEmptyInterface() {
				infoPrint("  Itab for dot type: %v\n", n)
				info.itabConvs = append(info.itabConvs, n)
			}
		case ir.OCLOSURE:
			// 访问闭包主体，将所有相关条目添加到
			// 外部函数字典（闭包将只使用
			// 外部函数字典）。
			cfunc := n.(*ir.ClosureExpr).Func
			for _, n1 := range cfunc.Body {
				ir.Visit(n1, visitFunc)
			}
			for _, n := range cfunc.Dcl {
				n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1)
			}
		case ir.OSWITCH:
			ss := n.(*ir.SwitchStmt)
			if ss.Tag != nil && ss.Tag.Op() == ir.OTYPESW &&
				!ss.Tag.(*ir.TypeSwitchGuard).X.Type().IsEmptyInterface() {
				for _, cc := range ss.Cases {
					for _, c := range cc.List {
						if c.Op() == ir.OTYPE && c.Type().HasShape() {
							// 来自非空接口的类型开关-可能需要itab。
							infoPrint("  Itab for type switch: %v\n", c)
							info.itabConvs = append(info.itabConvs, c)
							if info.type2switchType == nil {
								info.type2switchType = map[ir.Node]*types.Type{}
							}
							info.type2switchType[c] = ss.Tag.(*ir.TypeSwitchGuard).X.Type()
						}
					}
				}
			}
		}
		addType(info, n, n.Type())
	}

	for _, stmt := range st.Body {
		ir.Visit(stmt, visitFunc)
	}
	if infoPrintMode {
		for _, t := range info.derivedTypes {
			fmt.Printf("  Derived type %v\n", t)
		}
		fmt.Printf(">>> Done Instinfo\n")
	}
	info.startSubDict = len(info.shapeParams) + len(info.derivedTypes)
	info.startItabConv = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls)
	info.dictLen = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs)
}

// isBoundMethod如果se指示的选择是
// se的绑定方法，则返回true。X.se。X必须是形状类型（即直接从类型参数中替换）。如果
// isBoundMethod返回false，则所选内容必须是
// 结构类型的字段访问。
func isBoundMethod(info *dictInfo, se *ir.SelectorExpr) bool {
	bound := info.shapeToBound[se.X.Type()]
	return typecheck.Lookdot1(se, se.Sel, bound, bound.AllMethods(), 1) != nil
}

// addType将t添加到信息中。derivedTypes，如果它是参数化类型（不是
// 只是一个简单的形状），它不同于
// info上的任何现有类型。被嘲笑的类型。
func addType(info *dictInfo, n ir.Node, t *types.Type) {
	if t == nil || !t.HasShape() {
		return
	}
	if t.IsShape() {
		return
	}
	if t.Kind() == types.TFUNC && n != nil &&
		(t.Recv() != nil || n.Op() == ir.ONAME && n.Name().Class == ir.PFUNC) {
		// 不要使用命名泛型函数或方法的类型
		// 因为它是由其他类型参数参数化的。
		// /（它们都来自FUNCINST节点的参数。）
		return
	}
	if doubleCheck && !parameterizedBy(t, info.shapeParams) {
		base.Fatalf("adding type with invalid parameters %+v", t)
	}
	if t.Kind() == types.TSTRUCT && t.IsFuncArgStruct() {
		// 多个返回值不是相关的新类型（？）。
		return
	}
	// 忽略我们已经添加的派生类型。
	for _, et := range info.derivedTypes {
		if types.IdenticalStrict(t, et) {
			return
		}
	}
	info.derivedTypes = append(info.derivedTypes, t)
}

// 如果t由（最多）参数化，则ParameteredBy返回true。
func parameterizedBy(t *types.Type, params []*types.Type) bool {
	return parameterizedBy1(t, params, map[*types.Type]bool{})
}
func parameterizedBy1(t *types.Type, params []*types.Type, visited map[*types.Type]bool) bool {
	if visited[t] {
		return true
	}
	visited[t] = true

	if t.Sym() != nil && len(t.RParams()) > 0 {
		// 已实例化此定义的类型。检查实例化类型。
		for _, r := range t.RParams() {
			if !parameterizedBy1(r, params, visited) {
				return false
			}
		}
		return true
	}
	if t.IsShape() {
		// 检查t是否是范围内允许的参数之一。
		for _, p := range params {
			if p == t {
				return true
			}
		}
		// 在允许的参数列表中找不到t。
		return false

	}
	switch t.Kind() {
	case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN:
		return parameterizedBy1(t.Elem(), params, visited)

	case types.TMAP:
		return parameterizedBy1(t.Key(), params, visited) && parameterizedBy1(t.Elem(), params, visited)

	case types.TFUNC:
		return parameterizedBy1(t.TParams(), params, visited) && parameterizedBy1(t.Recvs(), params, visited) && parameterizedBy1(t.Params(), params, visited) && parameterizedBy1(t.Results(), params, visited)

	case types.TSTRUCT:
		for _, f := range t.Fields().Slice() {
			if !parameterizedBy1(f.Type, params, visited) {
				return false
			}
		}
		return true

	case types.TINTER:
		for _, f := range t.Methods().Slice() {
			if !parameterizedBy1(f.Type, params, visited) {
				return false
			}
		}
		return true

	case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64,
		types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64,
		types.TUINTPTR, types.TBOOL, types.TSTRING, types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128, types.TUNSAFEPTR:
		return true

	case types.TUNION:
		for i := 0; i < t.NumTerms(); i++ {
			tt, _ := t.Term(i)
			if !parameterizedBy1(tt, params, visited) {
				return false
			}
		}
		return true

	default:
		base.Fatalf("bad type kind %+v", t)
		return true
	}
}

// startClosures开始创建具有函数类型typ的闭包。它
// 根据类型类型创建所有形式参数和结果。返回时，
// 必须仍然填写闭包的主体和闭包变量，
// ir。调用UseClosure（）。
func startClosure(pos src.XPos, outer *ir.Func, typ *types.Type) (*ir.Func, []*types.Field, []*types.Field) {
	// 创建一个新的内部函数。
	fn := ir.NewClosureFunc(pos, outer != nil)
	ir.NameClosure(fn.OClosure, outer)

	// 建立正式的参数和返回列表。
	var formalParams []*types.Field  // 闭包参数
	var formalResults []*types.Field // 闭包返回
	for i := 0; i < typ.NumParams(); i++ {
		t := typ.Params().Field(i).Type
		arg := ir.NewNameAt(pos, typecheck.LookupNum("a", i))
		arg.Class = ir.PPARAM
		typed(t, arg)
		arg.Curfn = fn
		fn.Dcl = append(fn.Dcl, arg)
		f := types.NewField(pos, arg.Sym(), t)
		f.Nname = arg
		f.SetIsDDD(typ.Params().Field(i).IsDDD())
		formalParams = append(formalParams, f)
	}
	for i := 0; i < typ.NumResults(); i++ {
		t := typ.Results().Field(i).Type
		result := ir.NewNameAt(pos, typecheck.LookupNum("r", i)) // TODO:不需要名称？
		result.Class = ir.PPARAMOUT
		typed(t, result)
		result.Curfn = fn
		fn.Dcl = append(fn.Dcl, result)
		f := types.NewField(pos, result.Sym(), t)
		f.Nname = result
		formalResults = append(formalResults, f)
	}

	// 使用正确的签名构建一个内部函数。
	closureType := types.NewSignature(typ.Pkg(), nil, nil, formalParams, formalResults)
	typed(closureType, fn.Nname)
	typed(typ, fn.OClosure)
	fn.SetTypecheck(1)
	return fn, formalParams, formalResults

}

// AssertToBond返回一个新节点，该节点将具有接口类型的节点rcvr转换为
// dst接口类型。
func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node, dst *types.Type) ir.Node {
	if dst.HasShape() {
		ix := findDictType(info, dst)
		assert(ix >= 0)
		rt := getDictionaryType(info, dictVar, pos, ix)
		rcvr = ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, rcvr, rt)
		typed(dst, rcvr)
	} else {
		rcvr = ir.NewTypeAssertExpr(pos, rcvr, nil)
		typed(dst, rcvr)
	}
	return rcvr
}

// buildClosure2生成一个闭包来实现一个方法表达式m（通用形式x）
// 它的形状类型为receiver。如果接收方恰好是一个形状（即来自
// 类型参数），则闭包体将m.X（接收方）转换为
// 接口绑定类型，并使用剩余参数进行接口调用。
// 
// 返回的闭包被完全替换，并且已经完成了任何需要的
// 转换。
func (g *genInst) buildClosure2(info *instInfo, m ir.Node) ir.Node {
	outer := info.fun
	pos := m.Pos()
	typ := m.Type() // 闭包的类型

	fn, formalParams, formalResults := startClosure(pos, outer, typ)

	// 在外部函数
	dictVar := ir.CaptureName(pos, fn, info.dictParam)
	typed(types.Types[types.TUINTPTR], dictVar)

	// 中计算的捕获字典
	// 在闭包内生成要调用的参数。
	var args []ir.Node
	for i := 0; i < typ.NumParams(); i++ {
		args = append(args, formalParams[i].Nname.(*ir.Name))
	}

	// 构建调用本身。这涉及到使用字典将第一个参数转换为
	// 绑定类型（接口），然后使用剩余参数进行
	// 接口调用。
	var innerCall ir.Node
	rcvr := args[0]
	args = args[1:]
	assert(m.(*ir.SelectorExpr).X.Type().IsShape())
	dst := info.dictInfo.shapeToBound[m.(*ir.SelectorExpr).X.Type()]
	if m.(*ir.SelectorExpr).X.Type().IsInterface() {
		// 如果类型arg是一个接口（不寻常的情况），我们对
		// 类型绑定执行类型断言。
		rcvr = assertToBound(info, dictVar, pos, rcvr, dst)
	} else {
		rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, m, dst, false)
	}
	dot := ir.NewSelectorExpr(pos, ir.ODOTINTER, rcvr, m.(*ir.SelectorExpr).Sel)
	dot.Selection = typecheck.Lookdot1(dot, dot.Sel, dot.X.Type(), dot.X.Type().AllMethods(), 1)

	typed(dot.Selection.Type, dot)
	innerCall = ir.NewCallExpr(pos, ir.OCALLINTER, dot, args)
	t := m.Type()
	if t.NumResults() == 0 {
		innerCall.SetTypecheck(1)
	} else if t.NumResults() == 1 {
		typed(t.Results().Field(0).Type, innerCall)
	} else {
		typed(t.Results(), innerCall)
	}
	if len(formalResults) > 0 {
		innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall})
		innerCall.SetTypecheck(1)
	}
	fn.Body = []ir.Node{innerCall}

	// 我们已经完成了捕获的字典
	ir.FinishCaptureNames(pos, outer, fn)

	// 对闭包进行最后检查并返回它。
	return ir.UseClosure(fn.OClosure, typecheck.Target)
}
